#include <thread>
#include <chrono>
#include <iostream>
#include <filesystem>
#include <cstdio>

#include "MediaFileDbManager.h"
#include "ProtocolApi.h"
#include "ConfigApi.h"

using namespace ConfigApi;
using namespace Protocol;
namespace fs = std::filesystem;

MediaFileDbManager::Ptr MediaFileDbManager::Instance(){
    static auto ptr = std::make_shared<MediaFileDbManager>();
    return ptr;
}


MediaFileDbManager::MediaFileDbManager(){
    m_stopped = false;
    m_sync_task = std::async([this]{
        fileSyncToServerTask();
    });
}

MediaFileDbManager::~MediaFileDbManager(){
    m_stopped = true;
    std::unique_lock<std::mutex> lock(m_mtx);
    m_con_var.wait(lock);
}


bool MediaFileDbManager::Init(const std::string& db_file_path){
    m_storage_path = db_file_path;
    try{
        m_storager_ptr = std::make_shared<internal::storage_t<StorageType>>(db_file_path + "/" + SqliteDbName, 
            make_table("MediaFile",
                            make_column("id", &MediaFile::m_id, primary_key()),
                            make_column("room_id", &MediaFile::m_room_id),
                            make_column("file_path", &MediaFile::m_file_path),
                            make_column("channel", &MediaFile::m_channel), 
                            make_column("start_time", &MediaFile::m_start_time),
                            make_column("stop_time", &MediaFile::m_stop_time),
                            make_column("file_size", &MediaFile::m_file_size),
                            make_column("has_sync", &MediaFile::m_has_sync)));
        m_storager_ptr->sync_schema();
    }catch(const std::system_error& e){
        std::cout << e.what() << std::endl;
        m_storager_ptr.reset();
    }
    
    return true;
}

bool MediaFileDbManager::Reset(){
    tracef(" MediaFileDbManager  Reset");
    m_storager_ptr.reset();
    return true;
}

bool MediaFileDbManager::AddFileEntry(const MediaFile& file_entry){
    if(!m_storager_ptr) return false;
    try{
        m_storager_ptr->insert(file_entry);
    }catch(const std::system_error& e){
        std::cout << e.what() << std::endl;
        m_storager_ptr.reset();
    }

    return true;
}

bool MediaFileDbManager::ReplaceFileEntry(const MediaFile& file_entry){
    if(!m_storager_ptr) return false;
    try{
        m_storager_ptr->replace(file_entry);
    }catch(const std::system_error& e){
        std::cout << e.what() << std::endl;
        m_storager_ptr.reset();
    }
    return true;
}


void MediaFileDbManager::clearFile(){
    if(m_storager_ptr){
        fs::path mountPoint = m_storage_path;  // 替换为你的挂载点路径
        fs::space_info space = fs::space(mountPoint);

        // std::cout << "Available space on " << mountPoint << ":" << std::endl;
        // std::cout << "Capacity: " << space.capacity << " bytes" << std::endl;
        // std::cout << "Free: " << space.free << " bytes" << std::endl;
        // std::cout << "Available: " << space.available << " bytes" << std::endl;

        auto precent_ava = (space.free*100)/space.capacity;
        //std::cout<<" storager Available precent "<<precent_ava<<std::endl;

        //1.判断存储点是否已满   冗余空间小于20%
        if(precent_ava < 20){
            try{
                //2.删除清理文件, 让冗余空间大于20%            
                //按照录播文件的开始时间排序
                auto rows = m_storager_ptr->select(columns(&MediaFile::m_id, 
                    &MediaFile::m_start_time, &MediaFile::m_file_path), order_by(&MediaFile::m_start_time).asc());
                for(auto& row: rows){
                    auto file_id = get<0>(row);
                    auto file_path = get<2>(row);
                    //删除文件
                    std::remove(file_path.c_str());
                    //3.数据库表同步
                    m_storager_ptr->remove_all<MediaFile>(where(is_equal(&MediaFile::m_id, file_id)));
                    //4.检查冗余空间是否超过20%
                    space = fs::space(mountPoint);
                    precent_ava = (space.free*100)/space.capacity;
                    if(precent_ava >= 20){
                        //清除完毕
                        break;
                    }
                }
            }catch(const std::system_error& e){
                std::cout << e.what() << std::endl;
                m_storager_ptr.reset();
            }
            
        }        
    }   
}

void MediaFileDbManager::fileSyncToServerTask(){
    std::mutex mtx;
    std::condition_variable cv;

    std::this_thread::sleep_for(std::chrono::seconds(5));
    while(!m_stopped){
        if(m_storager_ptr){
            //1.查询未同步的录播文件,has_sync为false
            try{
                auto files = m_storager_ptr->get_all<MediaFile>(where(is_equal(&MediaFile::m_has_sync, false)));
                for(auto& row: files){
                    std::cout<<" start upload file "<<row.m_file_path<<std::endl;

                    //2.将录播文件信息上传到平台,平台进行完整性校验,校验失败则上传录播文件到平台
                    //校验成功则无需上传

                    Json::Value req;
                    req["Seq"] = 1;
                    req["Class"] = "Transcribe";
                    req["Method"] = "Check";
                    Json::Value param;
                    param["RoomId"] = row.m_room_id;
                    param["Channel"] = row.m_channel;
                    param["Record_start"] = row.m_start_time;
                    param["Record_end"] = row.m_stop_time;
                    req["Param"] = param;
                    bool need_upload = true;
                    {
                        std::mutex mtx;
                        std::condition_variable cond;
                        
                        auto session_ptr = std::make_shared<std::string>("session");
                        std::weak_ptr<std::string> weak_session = session_ptr;

                        IProtocolInterface::GetInstance()->CheckRecordFile(req, 
                            [weak_session, &need_upload, &cond](const Json::Value &request){
                            auto strong_self = weak_session.lock();
                            if (strong_self) {
                                tracef("!!!!!!! CheckRecordFile result %s", request.toStyledString().c_str());
                                if(request.isMember("Param")){
                                    if(request["Param"].isMember("Result")){
                                        need_upload = !request["Param"]["Result"].asBool();
                                    }
                                }
                                cond.notify_one();
                            }
                       });
                      
                       std::unique_lock<std::mutex> ulo(mtx);
                       if(std::cv_status::timeout == cond.wait_for(ulo, std::chrono::seconds(5))){
                            warnf("websocket  request  timeout");
                            continue;
                       }
                    }

                    //3.上传录播文件到服务器
                    if(need_upload){
                        Json::Value kv;
                        int result = 0;
                        std::string config_path = "URL";
                        bool ret = IConfigInterface::GetInstance()->Get(config_path, kv, result);

                        if(ret && kv.isMember("MediaServerUrl")){
                            std::string remote_addr  = kv["MediaServerUrl"].asString();
                            std::string  upload_url = "http://" + remote_addr + "/api/upload";
                            upload_url += "?room_id=" + row.m_room_id + "&channel="+ row.m_channel 
                            + "&file_size=" + std::to_string(row.m_file_size)
                            +  "&start_time=" + std::to_string(row.m_start_time)  + 
                            "&end_time=" + std::to_string(row.m_stop_time);

                            IProtocolInterface::GetInstance()->HttpUploadFile(upload_url, 
                            row.m_file_path, [&cv](const Fdt::SockException &ex){
                                if(ex.getErrCode() == Fdt::Err_success){
                                    std::cout<<" upload success "<<std::endl;
                                }else{
                                    std::cout<<" upload fail "<< ex.what()<<std::endl;
                                }
                                cv.notify_one();
                            }, true);

                            std::unique_lock<std::mutex> lock(mtx);
                            cv.wait(lock);
                        }
                    }else{
                        //平台校验成功无需上传
                        row.m_has_sync = true;
                        m_storager_ptr->replace(row);
                    }
                    
                }
            }catch(const std::system_error& e){
                std::cout << e.what() << std::endl;
                m_storager_ptr.reset();
            }

            clearFile();
        }
        
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
    m_con_var.notify_one();
}